package com.dajun.service.impl;

import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.dajun.contast.BusinessStatusContast;
import com.dajun.mapper.ApproverMapper;
import com.dajun.pojo.dto.OperationDto;
import com.dajun.pojo.dto.ProcessQueryDto;
import com.dajun.pojo.dto.ApplyDto;
import com.dajun.pojo.entity.Approver;
import com.dajun.pojo.vo.FlowableVo;
import com.dajun.result.Result;
import com.dajun.service.FlowableService;
import com.dajun.service.TaskApproverService;
import lombok.extern.slf4j.Slf4j;
import org.flowable.bpmn.model.EndEvent;
import org.flowable.bpmn.model.FlowElement;
import org.flowable.bpmn.model.Process;
import org.flowable.engine.*;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.Execution;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.engine.runtime.ProcessInstanceQuery;
import org.flowable.task.api.Task;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

@Service
@Slf4j
public class FlowableServiceImpl implements FlowableService {
    @Resource
    private RepositoryService repositoryService;
    @Resource
    private TaskService taskService;
    @Resource
    private RuntimeService runtimeService;
    @Resource
    private ApproverMapper approverMapper;
    @Resource
    private IdentityService identityService;
    @Resource
    private TaskApproverService taskApproverService;


    /**
     * 开始流程
     *
     * @param dto
     * @return
     */
    @Override
    @Transactional
    public Result start(ApplyDto dto) {
        String type = dto.getType();
        //封装流程变量
        Map<String, Object> map = new HashMap<>();
        map.put("user", dto.getUserId());
        map.put("days", dto.getDays());
        map.put("description", dto.getDetail());
        //流程状态
        map.put("businessStatus", BusinessStatusContast.PENDING);
        //并行网关模拟，nrOfApproved代表当前通过数，requiredApprovals代表标准值
        map.put("nrOfApproved",0);
        map.put("requiredApprovals",2);
        //动态审批人添加
        List<Approver> approvers = new ArrayList<>();
        for (Long approval : dto.getApprovals()) {
            Approver approver = approverMapper.selectById(approval);
            approvers.add(approver);
        }
        map.put("approvers", approvers);

        String key = "";
        //拼接流程文件key
        switch (type) {
            case "请假":
                key = "processes/leave.bpmn20.xml";
                break;
        }
        //根据流程类型选择模板
        Deployment deploy = repositoryService.createDeployment()
                .addClasspathResource(key)
                .deploy();
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().deploymentId(deploy.getId()).singleResult();
        String processDefinitionKey = processDefinition.getKey();

        //开始流程
        //设置流程发起人，开始流程
        //todo 测试用例，暂时写死，后续根据业务获取发起人信息
        identityService.setAuthenticatedUserId("1");
        runtimeService.startProcessInstanceByKey(processDefinitionKey, map);

        // todo 后续根据业务需求将数据存放到自己的数据库里

        return Result.success(map);
    }

    /**
     * 终止流程
     *
     * @param processInstanceId
     * @return
     */
    @Override
    public Result terminate(String processInstanceId) {
        String result = processInstanceId.replaceAll("^\"+|\"+$", "");
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(result).singleResult();
        String id = processInstance.getProcessInstanceId();
        if (processInstance != null) {
            //1、获取终止节点
            List<EndEvent> endNodes = findEndFlowElement(processInstance.getProcessDefinitionId(), repositoryService);
            String endId = endNodes.get(0).getId();
            //2、执行终止
            List<Execution> executions = runtimeService.createExecutionQuery().parentId(id).list();
            List<String> executionIds = new ArrayList<>();
            executions.forEach(execution -> executionIds.add(execution.getId()));
            runtimeService.createChangeActivityStateBuilder().moveExecutionsToSingleActivityId(executionIds, endId).changeState();
            return Result.success("成功终止: " + id);
        } else {
            return Result.success("不存在运行的流程实例processInstanceId: " + id + " ,请确认!");
        }
    }

    @Override
    public Result select(ProcessQueryDto dto) {
        String processDefinitionKey = dto.getProcessDefinitionKey();
        // 动态构建查询
        ProcessInstanceQuery query = runtimeService.createProcessInstanceQuery();
        if (StringUtils.isNotBlank(processDefinitionKey)) {
            query.processDefinitionKey(processDefinitionKey);
        }

        //后续可删除， 本人测试查询数据用
        if (query != null) {
            String processInstanceId = query.singleResult().getProcessInstanceId();
            //根据流程id获取当前流程任务
            List<Task> taskList = taskService.createTaskQuery().processInstanceId(processInstanceId).list();

            //根据流程id获取当前活动节点
            List<String> activeActivityIds = runtimeService.getActiveActivityIds(processInstanceId);
            System.out.println(activeActivityIds);

            //获取当前流程所有审批人信息
            List<String> approvers = (List<String>) runtimeService.getVariable(processInstanceId, "approvers");
            System.out.println(approvers);
        }

        // 执行查询并转换结果
        List<FlowableVo> vos = query.list()
                .stream()
                .map(this::convertToVo)
                .collect(Collectors.toList());

        return Result.success(vos);
    }

    @Override
    public Result operation(OperationDto dto) {
            //获取对应任务实例
            List<Task> taskList = taskService.createTaskQuery().processInstanceId(dto.getProcessInstanceId()).list();
            String taskId = taskList.get(0).getId();
            Task task = taskService.createTaskQuery().taskId(taskId).singleResult();

            if (task == null) {
                return Result.error("任务不存在或已完成");
            }
            //封装审核信息
            Map<String,Object> map = new HashMap<>();
            map.put("approved", dto.getIsVia());
            map.put("comment", dto.getReason());
            map.put("approvalTime", new Date());
            //todo 测试写死数据，后续修改为当前登陆人
            map.put("approvalAction", "1");

            //修改流程状态，推进流程
            taskService.complete(task.getId(),map);
            //todo 后续根据业务需求存放操作信息/操作完成后的通知

            return Result.success("审批通过");
    }

    // 公共转换方法
    private FlowableVo convertToVo(ProcessInstance instance) {
        FlowableVo vo = new FlowableVo();
        vo.setProcessDefinitionName(instance.getProcessDefinitionName());
        vo.setProcessDefinitionKey(instance.getProcessDefinitionKey());
        vo.setProcessInstanceId(instance.getProcessInstanceId());
        vo.setBusinessStatus(instance.getBusinessStatus());
        vo.setStartTime(instance.getStartTime());
        vo.setStartUserId(instance.getStartUserId());
        vo.setNodeName(instance.getName());
        return vo;
    }

    public List findEndFlowElement(String processDefId, RepositoryService repositoryService) {
        Process mainProcess = repositoryService.getBpmnModel(processDefId).getMainProcess();
        Collection<FlowElement> list = mainProcess.getFlowElements();
        if (CollectionUtils.isEmpty(list)) {
            return Collections.EMPTY_LIST;
        }
        return list.stream().filter(f -> f instanceof EndEvent).collect(Collectors.toList());
    }
}
